/**
 * WebSocket 封装类
 * 功能特性：
 * 1. 自动重连机制
 * 2. 心跳检测
 * 3. 消息队列管理
 * 4. 事件系统
 * 5. 连接状态管理
 * 6. 错误处理
 * 7. 日志系统
 */

class WebSocketClient {
  constructor(options = {}) {
    // 配置参数
    this.config = {
      url: options.url || "", // WebSocket 连接地址
      protocols: options.protocols || [], // 协议
      heartbeatInterval: options.heartbeatInterval || 30000, // 心跳间隔（毫秒）
      heartbeatMsg: options.heartbeatMsg || "ping", // 心跳消息
      reconnect: options.reconnect !== false, // 是否自动重连
      reconnectInterval: options.reconnectInterval || 3000, // 重连间隔（毫秒）
      reconnectMaxAttempts: options.reconnectMaxAttempts || 5, // 最大重连次数，0 表示无限次
      connectTimeout: options.connectTimeout || 10000, // 连接超时时间（毫秒）
      debug: options.debug || false, // 是否开启调试模式
      binaryType: options.binaryType || "arraybuffer", // 二进制类型
    };

    // 连接状态
    this.readyState = {
      CONNECTING: 0, // 正在连接
      OPEN: 1, // 已连接
      CLOSING: 2, // 正在关闭
      CLOSED: 3, // 已关闭
    };

    // 当前状态
    this.status = this.readyState.CLOSED;

    // WebSocket 实例
    this.ws = null;

    // 心跳定时器
    this.heartbeatTimer = null;
    this.heartbeatTimeoutTimer = null;

    // 重连相关
    this.reconnectTimer = null;
    this.reconnectAttempts = 0; // 当前重连次数
    this.isManualClose = false; // 是否手动关闭

    // 消息队列（连接建立前的消息）
    this.messageQueue = [];

    // 事件监听器
    this.eventListeners = {
      open: [], // 连接打开
      message: [], // 收到消息
      error: [], // 连接错误
      close: [], // 连接关闭
      reconnect: [], // 重连事件
      heartbeat: [], // 心跳事件
    };

    // 连接超时定时器
    this.connectTimeoutTimer = null;

    // 最后一次发送心跳的时间
    this.lastHeartbeatTime = 0;
  }

  /**
   * 连接 WebSocket
   * @param {String} url 连接地址（可选，如果构造时已传入）
   */
  connect(url) {
    if (url) {
      this.config.url = url;
    }

    if (!this.config.url) {
      this.log("error", "WebSocket URL 不能为空");
      return;
    }

    if (
      this.ws &&
      (this.status === this.readyState.CONNECTING ||
        this.status === this.readyState.OPEN)
    ) {
      this.log("warn", "WebSocket 已连接或正在连接中");
      return;
    }

    this.isManualClose = false;
    this.status = this.readyState.CONNECTING;
    this.log("info", `开始连接 WebSocket: ${this.config.url}`);

    // 设置连接超时
    this.connectTimeoutTimer = setTimeout(() => {
      if (this.status === this.readyState.CONNECTING) {
        this.log("error", "连接超时");
        this.close();
        this.handleReconnect();
      }
    }, this.config.connectTimeout);

    try {
      // 创建 WebSocket 连接
      // uni-app 环境
      // #ifdef APP-PLUS || H5 || MP
      this.ws = uni.connectSocket({
        url: this.config.url,
        protocols: this.config.protocols,
        success: () => {
          this.log("info", "WebSocket 连接创建成功");
        },
        fail: (err) => {
          this.log("error", "WebSocket 连接创建失败", err);
          this.handleError(err);
          this.handleReconnect();
        },
      });
      // #endif

      // 浏览器环境
      // #ifdef WEB
      if (typeof WebSocket !== "undefined" && !uni.connectSocket) {
        this.ws = new WebSocket(this.config.url, this.config.protocols);
        this.ws.binaryType = this.config.binaryType;
      }
      // #endif

      // 绑定事件
      this.bindEvents();
    } catch (error) {
      this.log("error", "创建 WebSocket 连接异常", error);
      clearTimeout(this.connectTimeoutTimer);
      this.handleError(error);
      this.handleReconnect();
    }
  }

  /**
   * 绑定 WebSocket 事件
   */
  bindEvents() {
    if (!this.ws) return;

    // 连接打开
    this.ws.onOpen = this.onOpen.bind(this);
    // 接收消息
    this.ws.onMessage = this.onMessage.bind(this);
    // 连接关闭
    this.ws.onClose = this.onClose.bind(this);
    // 连接错误
    this.ws.onError = this.onError.bind(this);
  }

  /**
   * 连接打开回调
   */
  onOpen(res) {
    clearTimeout(this.connectTimeoutTimer);
    this.status = this.readyState.OPEN;
    this.reconnectAttempts = 0; // 重置重连次数
    this.log("info", "WebSocket 连接已打开");

    // 开启心跳
    this.startHeartbeat();

    // 发送队列中的消息
    this.flushMessageQueue();

    // 触发 open 事件
    this.emit("open", res);
  }

  /**
   * 接收消息回调
   */
  onMessage(res) {
    const data = res.data;
    this.log("info", "收到消息:", data);

    // 如果收到心跳响应，重置心跳超时
    if (this.isHeartbeatMessage(data)) {
      this.log("info", "收到心跳响应");
      clearTimeout(this.heartbeatTimeoutTimer);
      this.emit("heartbeat", data);
      return;
    }

    // 触发 message 事件
    this.emit("message", data);
  }

  /**
   * 连接关闭回调
   */
  onClose(res) {
    this.status = this.readyState.CLOSED;
    this.log("info", "WebSocket 连接已关闭", res);

    // 清除定时器
    this.stopHeartbeat();
    clearTimeout(this.connectTimeoutTimer);

    // 触发 close 事件
    this.emit("close", res);

    // 自动重连
    if (!this.isManualClose) {
      this.handleReconnect();
    }
  }

  /**
   * 连接错误回调
   */
  onError(err) {
    this.log("error", "WebSocket 连接错误", err);
    this.handleError(err);
  }

  /**
   * 发送消息
   * @param {String|Object|ArrayBuffer} data 要发送的数据
   * @param {Boolean} forceQueue 是否强制加入队列
   */
  send(data, forceQueue = false) {
    if (this.status !== this.readyState.OPEN || forceQueue) {
      // 连接未打开，加入消息队列
      this.log("warn", "连接未打开，消息加入队列");
      this.messageQueue.push(data);
      return false;
    }

    try {
      let sendData = data;

      // 如果是对象，转为 JSON 字符串
      if (typeof data === "object" && !(data instanceof ArrayBuffer)) {
        sendData = JSON.stringify(data);
      }

      // uni-app 环境
      // #ifdef APP-PLUS || H5 || MP
      uni.sendSocketMessage({
        data: sendData,
        success: () => {
          this.log("info", "消息发送成功:", sendData);
        },
        fail: (err) => {
          this.log("error", "消息发送失败:", err);
          this.messageQueue.push(data); // 发送失败，加入队列
        },
      });
      // #endif

      // 浏览器环境
      // #ifdef WEB
      if (this.ws && this.ws.send) {
        this.ws.send(sendData);
        this.log("info", "消息发送成功:", sendData);
      }
      // #endif

      return true;
    } catch (error) {
      this.log("error", "发送消息异常:", error);
      this.messageQueue.push(data);
      return false;
    }
  }

  /**
   * 关闭连接
   * @param {Number} code 关闭代码
   * @param {String} reason 关闭原因
   */
  close(code = 1000, reason = "Normal closure") {
    this.isManualClose = true;
    this.status = this.readyState.CLOSING;

    // 清除所有定时器
    this.stopHeartbeat();
    clearTimeout(this.reconnectTimer);
    clearTimeout(this.connectTimeoutTimer);

    if (this.ws) {
      try {
        // uni-app 环境
        // #ifdef APP-PLUS || H5 || MP
        uni.closeSocket({
          code,
          reason,
          success: () => {
            this.log("info", "主动关闭连接");
          },
          fail: (err) => {
            this.log("error", "关闭连接失败", err);
          },
        });
        // #endif

        // 浏览器环境
        // #ifdef WEB
        if (this.ws.close) {
          this.ws.close(code, reason);
          this.log("info", "主动关闭连接");
        }
        // #endif
      } catch (error) {
        this.log("error", "关闭连接异常", error);
      }
    }

    this.ws = null;
    this.status = this.readyState.CLOSED;
  }

  /**
   * 重新连接
   */
  reconnect() {
    if (this.isManualClose) {
      this.log("warn", "手动关闭，不进行重连");
      return;
    }

    if (!this.config.reconnect) {
      this.log("warn", "未开启自动重连");
      return;
    }

    // 检查重连次数
    if (
      this.config.reconnectMaxAttempts > 0 &&
      this.reconnectAttempts >= this.config.reconnectMaxAttempts
    ) {
      this.log(
        "error",
        `已达到最大重连次数: ${this.config.reconnectMaxAttempts}`
      );
      return;
    }

    this.reconnectAttempts++;
    this.log(
      "info",
      `准备重连 (${this.reconnectAttempts}/${
        this.config.reconnectMaxAttempts || "∞"
      })`
    );

    // 触发重连事件
    this.emit("reconnect", {
      attempts: this.reconnectAttempts,
      maxAttempts: this.config.reconnectMaxAttempts,
    });

    // 延迟重连
    this.reconnectTimer = setTimeout(() => {
      this.log("info", "开始重连...");
      this.connect();
    }, this.config.reconnectInterval);
  }

  /**
   * 处理重连逻辑
   */
  handleReconnect() {
    // 清理当前连接
    if (this.ws) {
      this.ws = null;
    }

    this.reconnect();
  }

  /**
   * 开启心跳
   */
  startHeartbeat() {
    if (this.config.heartbeatInterval <= 0) {
      return;
    }

    this.stopHeartbeat();

    this.heartbeatTimer = setInterval(() => {
      if (this.status === this.readyState.OPEN) {
        this.sendHeartbeat();
      }
    }, this.config.heartbeatInterval);
  }

  /**
   * 停止心跳
   */
  stopHeartbeat() {
    if (this.heartbeatTimer) {
      clearInterval(this.heartbeatTimer);
      this.heartbeatTimer = null;
    }
    if (this.heartbeatTimeoutTimer) {
      clearTimeout(this.heartbeatTimeoutTimer);
      this.heartbeatTimeoutTimer = null;
    }
  }

  /**
   * 发送心跳
   */
  sendHeartbeat() {
    this.log("info", "发送心跳");
    this.lastHeartbeatTime = Date.now();

    // 发送心跳消息
    this.send(this.config.heartbeatMsg);

    // 设置心跳超时检测（如果超过心跳间隔的1.5倍还没收到响应，则认为连接异常）
    this.heartbeatTimeoutTimer = setTimeout(() => {
      this.log("warn", "心跳超时，断开连接");
      this.close();
      this.handleReconnect();
    }, this.config.heartbeatInterval * 1.5);
  }

  /**
   * 判断是否是心跳消息
   * @param {*} data 消息数据
   */
  isHeartbeatMessage(data) {
    // 可以根据实际业务自定义心跳响应的判断逻辑
    if (typeof data === "string") {
      return data === "pong" || data === this.config.heartbeatMsg;
    }

    // 如果是 JSON 格式，可以判断特定字段
    try {
      const parsed = JSON.parse(data);
      return parsed.type === "heartbeat" || parsed.type === "pong";
    } catch (e) {
      return false;
    }
  }

  /**
   * 发送队列中的消息
   */
  flushMessageQueue() {
    if (this.messageQueue.length === 0) {
      return;
    }

    this.log("info", `发送队列中的 ${this.messageQueue.length} 条消息`);

    while (this.messageQueue.length > 0) {
      const message = this.messageQueue.shift();
      this.send(message);
    }
  }

  /**
   * 清空消息队列
   */
  clearMessageQueue() {
    this.messageQueue = [];
    this.log("info", "消息队列已清空");
  }

  /**
   * 注册事件监听
   * @param {String} event 事件名称
   * @param {Function} callback 回调函数
   */
  on(event, callback) {
    if (!this.eventListeners[event]) {
      this.log("warn", `未知的事件类型: ${event}`);
      return;
    }

    if (typeof callback !== "function") {
      this.log("error", "回调必须是函数");
      return;
    }

    this.eventListeners[event].push(callback);
  }

  /**
   * 移除事件监听
   * @param {String} event 事件名称
   * @param {Function} callback 回调函数
   */
  off(event, callback) {
    if (!this.eventListeners[event]) {
      return;
    }

    if (callback) {
      // 移除特定回调
      const index = this.eventListeners[event].indexOf(callback);
      if (index > -1) {
        this.eventListeners[event].splice(index, 1);
      }
    } else {
      // 移除所有回调
      this.eventListeners[event] = [];
    }
  }

  /**
   * 触发事件
   * @param {String} event 事件名称
   * @param {*} data 事件数据
   */
  emit(event, data) {
    if (!this.eventListeners[event]) {
      return;
    }

    this.eventListeners[event].forEach((callback) => {
      try {
        callback(data);
      } catch (error) {
        this.log("error", `事件回调执行出错 [${event}]:`, error);
      }
    });
  }

  /**
   * 处理错误
   * @param {*} error 错误信息
   */
  handleError(error) {
    this.emit("error", error);
  }

  /**
   * 获取当前连接状态
   */
  getStatus() {
    return this.status;
  }

  /**
   * 获取状态描述
   */
  getStatusText() {
    const statusMap = {
      [this.readyState.CONNECTING]: "CONNECTING",
      [this.readyState.OPEN]: "OPEN",
      [this.readyState.CLOSING]: "CLOSING",
      [this.readyState.CLOSED]: "CLOSED",
    };
    return statusMap[this.status] || "UNKNOWN";
  }

  /**
   * 检查是否已连接
   */
  isConnected() {
    return this.status === this.readyState.OPEN;
  }

  /**
   * 获取重连次数
   */
  getReconnectAttempts() {
    return this.reconnectAttempts;
  }

  /**
   * 重置重连次数
   */
  resetReconnectAttempts() {
    this.reconnectAttempts = 0;
  }

  /**
   * 获取消息队列长度
   */
  getMessageQueueLength() {
    return this.messageQueue.length;
  }

  /**
   * 更新配置
   * @param {Object} options 新的配置
   */
  updateConfig(options) {
    this.config = {
      ...this.config,
      ...options,
    };
    this.log("info", "配置已更新", this.config);
  }

  /**
   * 销毁实例
   */
  destroy() {
    this.log("info", "销毁 WebSocket 实例");
    this.close();
    this.clearMessageQueue();

    // 清除所有事件监听
    Object.keys(this.eventListeners).forEach((event) => {
      this.eventListeners[event] = [];
    });

    this.ws = null;
  }

  /**
   * 日志输出
   * @param {String} level 日志级别
   * @param {String} message 日志消息
   * @param {*} data 附加数据
   */
  log(level, message, data) {
    if (!this.config.debug) {
      return;
    }

    const timestamp = new Date().toLocaleTimeString();
    const prefix = `[WebSocket ${timestamp}]`;

    switch (level) {
      case "info":
        console.log(prefix, message, data || "");
        break;
      case "warn":
        console.warn(prefix, message, data || "");
        break;
      case "error":
        console.error(prefix, message, data || "");
        break;
      default:
        console.log(prefix, message, data || "");
    }
  }
}

// 导出单例模式（可选）
let wsInstance = null;

/**
 * 创建 WebSocket 实例
 * @param {Object} options 配置选项
 * @returns {WebSocketClient} WebSocket 实例
 */
export function createWebSocket(options) {
  return new WebSocketClient(options);
}

/**
 * 获取单例实例
 * @param {Object} options 配置选项
 * @returns {WebSocketClient} WebSocket 单例实例
 */
export function getWebSocketInstance(options) {
  if (!wsInstance) {
    wsInstance = new WebSocketClient(options);
  }
  return wsInstance;
}

/**
 * 销毁单例实例
 */
export function destroyWebSocketInstance() {
  if (wsInstance) {
    wsInstance.destroy();
    wsInstance = null;
  }
}

export default WebSocketClient;
